/* tvbuff.c
 *
 * Testy, Virtual(-izable) Buffer of uint8_t*'s
 *
 * "Testy" -- the buffer gets mad when an attempt to access data
 *        beyond the bounds of the buffer. An exception is thrown.
 *
 * "Virtual" -- the buffer can have its own data, can use a subset of
 *        the data of a backing tvbuff, or can be a composite of
 *        other tvbuffs.
 *
 * Copyright (c) 2000 by Gilbert Ramirez <gram@alumni.rice.edu>
 *
 * Code to convert IEEE floating point formats to native floating point
 * derived from code Copyright (c) Ashok Narayanan, 2000
 *
 * Wireshark - Network traffic analyzer
 * By Gerald Combs <gerald@wireshark.org>
 * Copyright 1998 Gerald Combs
 *
 * SPDX-License-Identifier: GPL-2.0-or-later
 */

#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <errno.h>

#include <glib.h>

#include "wmem_strutils.h"

#include "pint.h"
#include "sign_ext.h"
#include "tvbuff.h"
#include "tvbuff-int.h"
#include "strutil.h"
#include "charsets.h"

#include "proto.h"

#include <time.h>

static uint64_t _tvb_get_bits64(tvbuff_t *tvb, unsigned bit_offset, const int total_no_of_bits);

static uint64_t _tvb_get_bits64_le(tvbuff_t *tvb, unsigned bit_offset, const int total_no_of_bits);

static inline int _tvb_captured_length_remaining(const tvbuff_t *tvb, const int offset);

static inline const uint8_t*ensure_contiguous(tvbuff_t *tvb, const int offset, const int length);

static inline uint8_t *tvb_get_raw_string(tvbuff_t *tvb, const int offset, const int length);

tvbuff_t *tvb_new(const struct tvb_ops *ops)
{
    tvbuff_t *tvb;
    size_t    size = ops->tvb_size;

//    ws_assert(size >= sizeof(*tvb));

    tvb = (tvbuff_t *) g_slice_alloc(size);

    tvb->next         = NULL;
    tvb->ops         = ops;
    tvb->initialized     = false;
    tvb->flags         = 0;
    tvb->length         = 0;
    tvb->reported_length     = 0;
    tvb->contained_length     = 0;
    tvb->real_data         = NULL;
    tvb->raw_offset         = -1;
    tvb->ds_tvb         = NULL;

    return tvb;
}

static void tvb_free_internal(tvbuff_t *tvb)
{
    size_t    size;

    DISSECTOR_ASSERT(tvb);

    if (tvb->ops->tvb_free)
        tvb->ops->tvb_free(tvb);

    size = tvb->ops->tvb_size;

    g_slice_free1(size, tvb);
}

/* XXX: just call tvb_free_chain();
 *      Not removed so that existing dissectors using tvb_free() need not be changed.
 *      I'd argue that existing calls to tvb_free() should have actually beeen
 *      calls to tvb_free_chain() although the calls were OK as long as no
 *      subsets, etc had been created on the tvb. */
void tvb_free(tvbuff_t *tvb)
{
    tvb_free_chain(tvb);
}

void tvb_free_chain(tvbuff_t *tvb)
{
    tvbuff_t *next_tvb;
    DISSECTOR_ASSERT(tvb);
    while (tvb) {
        next_tvb = tvb->next;
        tvb_free_internal(tvb);
        tvb  = next_tvb;
    }
}

tvbuff_t *tvb_new_chain(tvbuff_t *parent, tvbuff_t *backing)
{
    tvbuff_t *tvb = tvb_new_proxy(backing);

    tvb_add_to_chain(parent, tvb);
    return tvb;
}

void tvb_add_to_chain(tvbuff_t *parent, tvbuff_t *child)
{
    tvbuff_t *tmp;

    DISSECTOR_ASSERT(parent);
    DISSECTOR_ASSERT(child);

    while (child) {
        tmp   = child;
        child = child->next;

        tmp->next    = parent->next;
        parent->next = tmp;
    }
}

/*
 * Check whether that offset goes more than one byte past the
 * end of the buffer.
 *
 * If not, return 0; otherwise, return exception
 */
static inline int validate_offset(const tvbuff_t *tvb, const unsigned abs_offset)
{
    if (G_LIKELY(abs_offset <= tvb->length)) {
        /* It's OK. */
        return 0;
    }

    /*
     * It's not OK, but why?  Which boundaries is it
     * past?
     */
    if (abs_offset <= tvb->contained_length) {
        /*
         * It's past the captured length, but not past
         * the reported end of any parent tvbuffs from
         * which this is constructed, or the reported
         * end of this tvbuff, so it's out of bounds
         * solely because we're past the end of the
         * captured data.
         */
        return BoundsError;
    }

    /*
     * There's some actual packet boundary, not just the
     * artificial boundary imposed by packet slicing, that
     * we're past.
     */

    if (tvb->flags & TVBUFF_FRAGMENT) {
        /*
         * This tvbuff is the first fragment of a larger
         * packet that hasn't been reassembled, so we
         * assume that's the source of the problem - if
         * we'd reassembled the packet, we wouldn't have
         * gone past the end.
         *
         * That might not be true, but for at least
         * some forms of reassembly, such as IP
         * reassembly, you don't know how big the
         * reassembled packet is unless you reassemble
         * it, so, in those cases, we can't determine
         * whether we would have gone past the end
         * had we reassembled the packet.
         */
        return FragmentBoundsError;
    }

    /* OK, we're not an unreassembled fragment (that we know of). */
    if (abs_offset <= tvb->reported_length) {
        /*
         * We're within the bounds of what this tvbuff
         * purportedly contains, based on some length
         * value, but we're not within the bounds of
         * something from which this tvbuff was
         * extracted, so that length value ran past
         * the end of some parent tvbuff.
         */
        return ContainedBoundsError;
    }

    /*
     * OK, it looks as if we ran past the claimed length
     * of data.
     */
    return ReportedBoundsError;
}

static inline int compute_offset(const tvbuff_t *tvb, const int offset, unsigned *offset_ptr)
{
    if (offset >= 0) {
        /* Positive offset - relative to the beginning of the packet. */
        if (G_LIKELY((unsigned) offset <= tvb->length)) {
            *offset_ptr = offset;
        } else if ((unsigned) offset <= tvb->contained_length) {
            return BoundsError;
        } else if (tvb->flags & TVBUFF_FRAGMENT) {
            return FragmentBoundsError;
        } else if ((unsigned) offset <= tvb->reported_length) {
            return ContainedBoundsError;
        } else {
            return ReportedBoundsError;
        }
    }
    else {
        /* Negative offset - relative to the end of the packet. */
        if (G_LIKELY((unsigned) -offset <= tvb->length)) {
            *offset_ptr = tvb->length + offset;
        } else if ((unsigned) -offset <= tvb->contained_length) {
            return BoundsError;
        } else if (tvb->flags & TVBUFF_FRAGMENT) {
            return FragmentBoundsError;
        } else if ((unsigned) -offset <= tvb->reported_length) {
            return ContainedBoundsError;
        } else {
            return ReportedBoundsError;
        }
    }

    return 0;
}

static inline int compute_offset_and_remaining(const tvbuff_t *tvb, const int offset, unsigned *offset_ptr, unsigned *rem_len)
{
    int exception;

    exception = compute_offset(tvb, offset, offset_ptr);
    if (!exception)
        *rem_len = tvb->length - *offset_ptr;

    return exception;
}

/* Computes the absolute offset and length based on a possibly-negative offset
 * and a length that is possible -1 (which means "to the end of the data").
 * Returns integer indicating whether the offset is in bounds (0) or
 * not (exception number). The integer ptrs are modified with the new offset,
 * captured (available) length, and contained length (amount that's present
 * in the parent tvbuff based on its reported length).
 * No exception is thrown; on success, we return 0, otherwise we return an
 * exception for the caller to throw if appropriate.
 *
 * XXX - we return success (0), if the offset is positive and right
 * after the end of the tvbuff (i.e., equal to the length).  We do this
 * so that a dissector constructing a subset tvbuff for the next protocol
 * will get a zero-length tvbuff, not an exception, if there's no data
 * left for the next protocol - we want the next protocol to be the one
 * that gets an exception, so the error is reported as an error in that
 * protocol rather than the containing protocol.  */
static inline int check_offset_length_no_exception(const tvbuff_t *tvb,
                 const int offset, int const length_val,
                 unsigned *offset_ptr, unsigned *length_ptr)
{
    unsigned end_offset;
    int   exception;

    DISSECTOR_ASSERT(offset_ptr);
    DISSECTOR_ASSERT(length_ptr);

    /* Compute the offset */
    exception = compute_offset(tvb, offset, offset_ptr);
    if (exception)
        return exception;

    if (length_val < -1) {
        /* XXX - ReportedBoundsError? */
        return BoundsError;
    }

    /* Compute the length */
    if (length_val == -1)
        *length_ptr = tvb->length - *offset_ptr;
    else
        *length_ptr = length_val;

    /*
     * Compute the offset of the first byte past the length.
     */
    end_offset = *offset_ptr + *length_ptr;

    /*
     * Check for an overflow
     */
    if (end_offset < *offset_ptr)
        return BoundsError;

    return validate_offset(tvb, end_offset);
}

/* Checks (+/-) offset and length and throws an exception if
 * either is out of bounds. Sets integer ptrs to the new offset
 * and length. */
static inline void check_offset_length(const tvbuff_t *tvb,
            const int offset, int const length_val,
            unsigned *offset_ptr, unsigned *length_ptr)
{
    int exception;

    exception = check_offset_length_no_exception(tvb, offset, length_val, offset_ptr, length_ptr);
//    if (exception)
//        THROW(exception);
}

void tvb_check_offset_length(const tvbuff_t *tvb,
                const int offset, int const length_val,
                unsigned *offset_ptr, unsigned *length_ptr)
{
    check_offset_length(tvb, offset, length_val, offset_ptr, length_ptr);
}

static const unsigned char left_aligned_bitmask[] = {
    0xff,
    0x80,
    0xc0,
    0xe0,
    0xf0,
    0xf8,
    0xfc,
    0xfe
};

tvbuff_t *tvb_new_octet_aligned(tvbuff_t *tvb, uint32_t bit_offset, int32_t no_of_bits)
{
    tvbuff_t     *sub_tvb = NULL;
    uint32_t      byte_offset;
    int32_t       datalen, i;
    uint8_t       left, right, remaining_bits, *buf;
    const uint8_t *data;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    byte_offset = bit_offset >> 3;
    left = bit_offset % 8; /* for left-shifting */
    right = 8 - left; /* for right-shifting */

    if (no_of_bits == -1) {
        datalen = _tvb_captured_length_remaining(tvb, byte_offset);
        remaining_bits = 0;
    } else {
        datalen = no_of_bits >> 3;
        remaining_bits = no_of_bits % 8;
        if (remaining_bits) {
            datalen++;
        }
    }

    /* already aligned -> shortcut */
    if ((left == 0) && (remaining_bits == 0)) {
        return tvb_new_subset_length_caplen(tvb, byte_offset, datalen, datalen);
    }

    DISSECTOR_ASSERT(datalen>0);

    /* if at least one trailing byte is available, we must use the content
    * of that byte for the last shift (i.e. tvb_get_ptr() must use datalen + 1
    * if non extra byte is available, the last shifted byte requires
    * special treatment
    */
    if (_tvb_captured_length_remaining(tvb, byte_offset) > datalen) {
        data = ensure_contiguous(tvb, byte_offset, datalen + 1); /* tvb_get_ptr */

        /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
        buf = (uint8_t *)g_malloc(datalen);

        /* shift tvb data bit_offset bits to the left */
        for (i = 0; i < datalen; i++)
            buf[i] = (data[i] << left) | (data[i+1] >> right);
    } else {
        data = ensure_contiguous(tvb, byte_offset, datalen); /* tvb_get_ptr() */

        /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
        buf = (uint8_t *)g_malloc(datalen);

        /* shift tvb data bit_offset bits to the left */
        for (i = 0; i < (datalen-1); i++)
            buf[i] = (data[i] << left) | (data[i+1] >> right);
        buf[datalen-1] = data[datalen-1] << left; /* set last octet */
    }
    buf[datalen-1] &= left_aligned_bitmask[remaining_bits];

    sub_tvb = tvb_new_child_real_data(tvb, buf, datalen, datalen);
    tvb_set_free_cb(sub_tvb, g_free);

    return sub_tvb;
}

tvbuff_t *tvb_new_octet_right_aligned(tvbuff_t *tvb, uint32_t bit_offset, int32_t no_of_bits)
{
    tvbuff_t     *sub_tvb = NULL;
    uint32_t      byte_offset;
    int           src_len, dst_len, i;
    uint8_t       left, right, remaining_bits, *buf;
    const uint8_t *data;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    byte_offset = bit_offset / 8;
    /* right shift to put bits in place and discard least significant bits */
    right = bit_offset % 8;
    /* left shift to get most significant bits from next octet */
    left = 8 - right;

    if (no_of_bits == -1) {
        dst_len = _tvb_captured_length_remaining(tvb, byte_offset);
        remaining_bits = 0;
    } else {
        dst_len = no_of_bits / 8;
        remaining_bits = no_of_bits % 8;
        if (remaining_bits) {
            dst_len++;
        }
    }

    /* already aligned -> shortcut */
    if ((right == 0) && (remaining_bits == 0)) {
        return tvb_new_subset_length_caplen(tvb, byte_offset, dst_len, dst_len);
    }

    DISSECTOR_ASSERT(dst_len>0);

    if (_tvb_captured_length_remaining(tvb, byte_offset) > dst_len) {
        /* last octet will get data from trailing octet */
        src_len = dst_len + 1;
    } else {
        /* last octet will be zero padded */
        src_len = dst_len;
    }

    data = ensure_contiguous(tvb, byte_offset, src_len); /* tvb_get_ptr */

    /* Do this allocation AFTER tvb_get_ptr() (which could throw an exception) */
    buf = (uint8_t *)g_malloc(dst_len);

    for (i = 0; i < (dst_len - 1); i++)
        buf[i] = (data[i] >> right) | (data[i+1] << left);

    /* Special handling for last octet */
    buf[i] = (data[i] >> right);
    /* Shift most significant bits from trailing octet if available */
    if (src_len > dst_len)
        buf[i] |= (data[i+1] << left);
    /* Preserve only remaining bits in last octet if not multiple of 8 */
    if (remaining_bits)
        buf[i] &= ((1 << remaining_bits) - 1);

    sub_tvb = tvb_new_child_real_data(tvb, buf, dst_len, dst_len);
    tvb_set_free_cb(sub_tvb, g_free);

    return sub_tvb;
}

static tvbuff_t *tvb_generic_clone_offset_len(tvbuff_t *tvb, unsigned offset, unsigned len)
{
    tvbuff_t *cloned_tvb;
    uint8_t *data;

    DISSECTOR_ASSERT(tvb_bytes_exist(tvb, offset, len));

    data = (uint8_t *) g_malloc(len);

    (void*)tvb_memcpy(tvb, (void *)data, (const int)offset, (size_t)len);

    cloned_tvb = tvb_new_real_data(data, len, len);
    tvb_set_free_cb(cloned_tvb, g_free);

    return cloned_tvb;
}

tvbuff_t *tvb_clone_offset_len(tvbuff_t *tvb, unsigned offset, unsigned len)
{
    if (tvb->ops->tvb_clone) {
        tvbuff_t *cloned_tvb;

        cloned_tvb = tvb->ops->tvb_clone(tvb, offset, len);
        if (cloned_tvb)
            return cloned_tvb;
    }

    return tvb_generic_clone_offset_len(tvb, offset, len);
}

tvbuff_t *tvb_clone(tvbuff_t *tvb)
{
    return tvb_clone_offset_len(tvb, 0, tvb->length);
}

unsigned tvb_captured_length(const tvbuff_t *tvb)
{
    DISSECTOR_ASSERT(tvb && tvb->initialized);

    return tvb->length;
}

/* For tvbuff internal use */
static inline int _tvb_captured_length_remaining(const tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0, rem_length;
    int   exception;

    exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
    if (exception)
        return 0;

    return rem_length;
}

int tvb_captured_length_remaining(const tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0, rem_length;
    int   exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
    if (exception)
        return 0;

    return rem_length;
}

unsigned tvb_ensure_captured_length_remaining(const tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0, rem_length = 0;
    int   exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &rem_length);
//    if (exception)
//        THROW(exception);

    if (rem_length == 0) {
        /*
         * This routine ensures there's at least one byte available.
         * There aren't any bytes available, so throw the appropriate
         * exception.
         */
//        if (abs_offset < tvb->contained_length) {
//            THROW(BoundsError);
//        } else if (tvb->flags & TVBUFF_FRAGMENT) {
//            THROW(FragmentBoundsError);
//        } else if (abs_offset < tvb->reported_length) {
//            THROW(ContainedBoundsError);
//        } else {
//            THROW(ReportedBoundsError);
//        }
    }
    return rem_length;
}

/* Validates that 'length' bytes are available starting from
 * offset (pos/neg). Does not throw an exception. */
bool tvb_bytes_exist(const tvbuff_t *tvb, const int offset, const int length)
{
    unsigned abs_offset = 0, abs_length;
    int   exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    /*
     * Negative lengths are not possible and indicate a bug (e.g. arithmetic
     * error or an overly large value from packet data).
     */
    if (length < 0)
        return false;

    exception = check_offset_length_no_exception(tvb, offset, length, &abs_offset, &abs_length);
    if (exception)
        return false;

    return true;
}

/* Validates that 'length' bytes, where 'length' is a 64-bit unsigned
 * integer, are available starting from offset (pos/neg). Throws an
 * exception if they aren't. */
void tvb_ensure_bytes_exist64(const tvbuff_t *tvb, const int offset, const uint64_t length)
{
    /*
     * Make sure the value fits in a signed integer; if not, assume
     * that means that it's too big.
     */
//    if (length > INT_MAX) {
//        THROW(ReportedBoundsError);
//    }

    /* OK, now cast it and try it with tvb_ensure_bytes_exist(). */
    tvb_ensure_bytes_exist(tvb, offset, (int)length);
}

/* Validates that 'length' bytes are available starting from
 * offset (pos/neg). Throws an exception if they aren't. */
void tvb_ensure_bytes_exist(const tvbuff_t *tvb, const int offset, const int length)
{
    unsigned real_offset, end_offset;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    /*
     * -1 doesn't mean "until end of buffer", as that's pointless
     * for this routine.  We must treat it as a Really Large Positive
     * Number, so that we throw an exception; we throw
     * ReportedBoundsError, as if it were past even the end of a
     * reassembled packet, and past the end of even the data we
     * didn't capture.
     *
     * We do the same with other negative lengths.
     */
//    if (length < 0) {
//        THROW(ReportedBoundsError);
//    }

    /* XXX: Below this point could be replaced with a call to
     * check_offset_length with no functional change, however this is a
     * *very* hot path and check_offset_length is not well-optimized for
     * this case, so we eat some code duplication for a lot of speedup. */

    if (offset >= 0) {
        /* Positive offset - relative to the beginning of the packet. */
        if (G_LIKELY((unsigned) offset <= tvb->length)) {
            real_offset = offset;
        }/* else if ((unsigned) offset <= tvb->contained_length) {
            THROW(BoundsError);
        } else if (tvb->flags & TVBUFF_FRAGMENT) {
            THROW(FragmentBoundsError);
        } else if ((unsigned) offset <= tvb->reported_length) {
            THROW(ContainedBoundsError);
        } else {
            THROW(ReportedBoundsError);
        }*/
    }
    else {
        /* Negative offset - relative to the end of the packet. */
        if (G_LIKELY((unsigned) -offset <= tvb->length)) {
            real_offset = tvb->length + offset;
        }/* else if ((unsigned) -offset <= tvb->contained_length) {
            THROW(BoundsError);
        } else if (tvb->flags & TVBUFF_FRAGMENT) {
            THROW(FragmentBoundsError);
        } else if ((unsigned) -offset <= tvb->reported_length) {
            THROW(ContainedBoundsError);
        } else {
            THROW(ReportedBoundsError);
        }*/
    }

    /*
     * Compute the offset of the first byte past the length.
     */
    end_offset = real_offset + length;

    /*
     * Check for an overflow
     */
//    if (end_offset < real_offset)
//        THROW(BoundsError);

    if (G_LIKELY(end_offset <= tvb->length))
        return;
//    else if (end_offset <= tvb->contained_length)
//        THROW(BoundsError);
//    else if (tvb->flags & TVBUFF_FRAGMENT)
//        THROW(FragmentBoundsError);
//    else if (end_offset <= tvb->reported_length)
//        THROW(ContainedBoundsError);
//    else
//        THROW(ReportedBoundsError);
}

bool tvb_offset_exists(const tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0;
    int   exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    exception = compute_offset(tvb, offset, &abs_offset);
    if (exception)
        return false;

    /* compute_offset only throws an exception on >, not >= because of the
     * comment above check_offset_length_no_exception, but here we want the
     * opposite behaviour so we check ourselves... */
    return abs_offset < tvb->length;
}

unsigned tvb_reported_length(const tvbuff_t *tvb)
{
    DISSECTOR_ASSERT(tvb && tvb->initialized);

    return tvb->reported_length;
}

int tvb_reported_length_remaining(const tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0;
    int   exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    exception = compute_offset(tvb, offset, &abs_offset);
    if (exception)
        return 0;

    if (tvb->reported_length >= abs_offset)
        return tvb->reported_length - abs_offset;
    else
        return 0;
}

unsigned tvb_ensure_reported_length_remaining(const tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0;
    int   exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    exception = compute_offset(tvb, offset, &abs_offset);
//    if (exception)
//        THROW(exception);

    if (tvb->reported_length >= abs_offset)
        return tvb->reported_length - abs_offset;
//    else
//        THROW(ReportedBoundsError);
    return 0;
}

/* Set the reported length of a tvbuff to a given value; used for protocols
 * whose headers contain an explicit length and where the calling
 * dissector's payload may include padding as well as the packet for
 * this protocol.
 * Also adjusts the available and contained length. */
void tvb_set_reported_length(tvbuff_t *tvb, const unsigned reported_length)
{
    DISSECTOR_ASSERT(tvb && tvb->initialized);

//    if (reported_length > tvb->reported_length)
//        THROW(ReportedBoundsError);

    tvb->reported_length = reported_length;
    if (reported_length < tvb->length)
        tvb->length = reported_length;
    if (reported_length < tvb->contained_length)
        tvb->contained_length = reported_length;
}

/* Repair a tvbuff where the captured length is greater than the
 * reported length; such a tvbuff makes no sense, as it's impossible
 * to capture more data than is in the packet.
 */
void
tvb_fix_reported_length(tvbuff_t *tvb)
{
    DISSECTOR_ASSERT(tvb && tvb->initialized);
    DISSECTOR_ASSERT(tvb->reported_length < tvb->length);

    tvb->reported_length = tvb->length;
    if (tvb->contained_length < tvb->length)
        tvb->contained_length = tvb->length;
}

unsigned
tvb_offset_from_real_beginning_counter(const tvbuff_t *tvb, const unsigned counter)
{
    if (tvb->ops->tvb_offset)
        return tvb->ops->tvb_offset(tvb, counter);

    DISSECTOR_ASSERT_NOT_REACHED();
    return 0;
}

unsigned
tvb_offset_from_real_beginning(const tvbuff_t *tvb)
{
    return tvb_offset_from_real_beginning_counter(tvb, 0);
}

static inline const uint8_t*
ensure_contiguous_no_exception(tvbuff_t *tvb, const int offset, const int length, int *pexception)
{
    unsigned abs_offset = 0, abs_length = 0;
    int   exception;

    exception = check_offset_length_no_exception(tvb, offset, length, &abs_offset, &abs_length);
    if (exception) {
        if (pexception)
            *pexception = exception;
        return NULL;
    }

    /*
     * Special case: if the caller (e.g. tvb_get_ptr) requested no data,
     * then it is acceptable to have an empty tvb (!tvb->real_data).
     */
    if (length == 0) {
        return NULL;
    }

    /*
     * We know that all the data is present in the tvbuff, so
     * no exceptions should be thrown.
     */
    if (tvb->real_data)
        return tvb->real_data + abs_offset;

    if (tvb->ops->tvb_get_ptr)
        return tvb->ops->tvb_get_ptr(tvb, abs_offset, abs_length);

    DISSECTOR_ASSERT_NOT_REACHED();
    return NULL;
}

static inline const uint8_t*
ensure_contiguous(tvbuff_t *tvb, const int offset, const int length)
{
    int           exception = 0;
    const uint8_t *p;

    p = ensure_contiguous_no_exception(tvb, offset, length, &exception);
//    if (p == NULL && length != 0) {
//        DISSECTOR_ASSERT(exception > 0);
//        THROW(exception);
//    }
    return p;
}

static inline const uint8_t*
fast_ensure_contiguous(tvbuff_t *tvb, const int offset, const unsigned length)
{
    unsigned end_offset;
    unsigned u_offset;

    DISSECTOR_ASSERT(tvb && tvb->initialized);
    /* We don't check for overflow in this fast path so we only handle simple types */
    DISSECTOR_ASSERT(length <= 8);

    if (offset < 0 || !tvb->real_data) {
        return ensure_contiguous(tvb, offset, length);
    }

    u_offset = offset;
    end_offset = u_offset + length;

    if (G_LIKELY(end_offset <= tvb->length)) {
        return tvb->real_data + u_offset;
    }/* else if (end_offset <= tvb->contained_length) {
        THROW(BoundsError);
    } else if (tvb->flags & TVBUFF_FRAGMENT) {
        THROW(FragmentBoundsError);
    } else if (end_offset <= tvb->reported_length) {
        THROW(ContainedBoundsError);
    } else {
        THROW(ReportedBoundsError);
    }*/
    /* not reached */
    return NULL;
}



/************** ACCESSORS **************/

void *tvb_memcpy(tvbuff_t *tvb, void *target, const int offset, size_t length)
{
    unsigned    abs_offset = 0, abs_length = 0;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    /*
     * XXX - we should eliminate the "length = -1 means 'to the end
     * of the tvbuff'" convention, and use other means to achieve
     * that; this would let us eliminate a bunch of checks for
     * negative lengths in cases where the protocol has a 32-bit
     * length field.
     *
     * Allowing -1 but throwing an assertion on other negative
     * lengths is a bit more work with the length being a size_t;
     * instead, we check for a length <= 2^31-1.
     */
    DISSECTOR_ASSERT(length <= 0x7FFFFFFF);
    check_offset_length(tvb, offset, (int) length, &abs_offset, &abs_length);

    if (target && tvb->real_data) {
        return memcpy(target, tvb->real_data + abs_offset, abs_length);
    }

    if (target && tvb->ops->tvb_memcpy)
        return tvb->ops->tvb_memcpy(tvb, target, abs_offset, abs_length);

    /*
     * If the length is 0, there's nothing to do.
     * (tvb->real_data could be null if it's allocated with
     * a size of length.)
     */
    if (length != 0) {
        /*
         * XXX, fallback to slower method
         */
        DISSECTOR_ASSERT_NOT_REACHED();
    }
    return NULL;
}


/*
 * XXX - this doesn't treat a length of -1 as an error.
 * If it did, this could replace some code that calls
 * "tvb_ensure_bytes_exist()" and then allocates a buffer and copies
 * data to it.
 *
 * "composite_get_ptr()" depends on -1 not being
 * an error; does anything else depend on this routine treating -1 as
 * meaning "to the end of the buffer"?
 *
 * If scope is NULL, memory is allocated with g_malloc() and user must
 * explicitly free it with g_free().
 * If scope is not NULL, memory is allocated with the corresponding pool
 * lifetime.
 */
void *tvb_memdup(tvbuff_t *tvb, const int offset, size_t length)
{
    unsigned  abs_offset = 0, abs_length = 0;
    void  *duped;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    check_offset_length(tvb, offset, (int) length, &abs_offset, &abs_length);

    if (abs_length == 0)
        return NULL;

    duped = malloc(abs_length);
    return tvb_memcpy(tvb, duped, abs_offset, abs_length);
}



const uint8_t* tvb_get_ptr(tvbuff_t *tvb, const int offset, const int length)
{
    return ensure_contiguous(tvb, offset, length);
}

/* ---------------- */
uint8_t tvb_get_uint8(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 1);
    return *ptr;
}

int8_t tvb_get_int8(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 1);
    return *ptr;
}

uint16_t tvb_get_ntohs(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 2);
    return pntoh16(ptr);
}

int16_t tvb_get_ntohis(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 2);
    return pntoh16(ptr);
}

uint32_t tvb_get_ntoh24(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 3);
    return pntoh24(ptr);
}

int32_t tvb_get_ntohi24(tvbuff_t *tvb, const int offset)
{
    uint32_t ret;

    ret = ws_sign_ext32(tvb_get_ntoh24(tvb, offset), 24);

    return (int32_t)ret;
}

uint32_t tvb_get_ntohl(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 4);
    return pntoh32(ptr);
}

int32_t tvb_get_ntohil(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 4);
    return pntoh32(ptr);
}

uint64_t tvb_get_ntoh40(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 5);
    return pntoh40(ptr);
}

int64_t tvb_get_ntohi40(tvbuff_t *tvb, const int offset)
{
    uint64_t ret;

    ret = ws_sign_ext64(tvb_get_ntoh40(tvb, offset), 40);

    return (int64_t)ret;
}

uint64_t tvb_get_ntoh48(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 6);
    return pntoh48(ptr);
}

int64_t tvb_get_ntohi48(tvbuff_t *tvb, const int offset)
{
    uint64_t ret;

    ret = ws_sign_ext64(tvb_get_ntoh48(tvb, offset), 48);

    return (int64_t)ret;
}

uint64_t tvb_get_ntoh56(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 7);
    return pntoh56(ptr);
}

int64_t tvb_get_ntohi56(tvbuff_t *tvb, const int offset)
{
    uint64_t ret;

    ret = ws_sign_ext64(tvb_get_ntoh56(tvb, offset), 56);

    return (int64_t)ret;
}

uint64_t tvb_get_ntoh64(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 8);
    return pntoh64(ptr);
}

int64_t tvb_get_ntohi64(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 8);
    return pntoh64(ptr);
}

uint16_t tvb_get_uint16(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohs(tvb, offset);
    } else {
        return tvb_get_ntohs(tvb, offset);
    }
}

int16_t tvb_get_int16(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohis(tvb, offset);
    } else {
        return tvb_get_ntohis(tvb, offset);
    }
}

uint32_t tvb_get_uint24(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letoh24(tvb, offset);
    } else {
        return tvb_get_ntoh24(tvb, offset);
    }
}

int32_t tvb_get_int24(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohi24(tvb, offset);
    } else {
        return tvb_get_ntohi24(tvb, offset);
    }
}

uint32_t tvb_get_uint32(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohl(tvb, offset);
    } else {
        return tvb_get_ntohl(tvb, offset);
    }
}

int32_t tvb_get_int32(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohil(tvb, offset);
    } else {
        return tvb_get_ntohil(tvb, offset);
    }
}

uint64_t tvb_get_uint40(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letoh40(tvb, offset);
    } else {
        return tvb_get_ntoh40(tvb, offset);
    }
}

int64_t tvb_get_int40(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohi40(tvb, offset);
    } else {
        return tvb_get_ntohi40(tvb, offset);
    }
}

uint64_t tvb_get_uint48(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letoh48(tvb, offset);
    } else {
        return tvb_get_ntoh48(tvb, offset);
    }
}

int64_t tvb_get_int48(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohi48(tvb, offset);
    } else {
        return tvb_get_ntohi48(tvb, offset);
    }
}

uint64_t tvb_get_uint56(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letoh56(tvb, offset);
    } else {
        return tvb_get_ntoh56(tvb, offset);
    }
}

int64_t tvb_get_int56(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohi56(tvb, offset);
    } else {
        return tvb_get_ntohi56(tvb, offset);
    }
}

uint64_t tvb_get_uint64(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letoh64(tvb, offset);
    } else {
        return tvb_get_ntoh64(tvb, offset);
    }
}

int64_t tvb_get_int64(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohi64(tvb, offset);
    } else {
        return tvb_get_ntohi64(tvb, offset);
    }
}

float tvb_get_ieee_float(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohieee_float(tvb, offset);
    } else {
        return tvb_get_ntohieee_float(tvb, offset);
    }
}

double tvb_get_ieee_double(tvbuff_t *tvb, const int offset, const unsigned encoding)
{
    if (encoding & ENC_LITTLE_ENDIAN) {
        return tvb_get_letohieee_double(tvb, offset);
    } else {
        return tvb_get_ntohieee_double(tvb, offset);
    }
}

/*
 * Fetches an IEEE single-precision floating-point number, in
 * big-endian form, and returns a "float".
 *
 * XXX - should this be "double", in case there are IEEE single-
 * precision numbers that won't fit in some platform's native
 * "float" format?
 */
float tvb_get_ntohieee_float(tvbuff_t *tvb, const int offset)
{
#if defined(vax)
    return get_ieee_float(tvb_get_ntohl(tvb, offset));
#else
    union {
        float    f;
        uint32_t w;
    } ieee_fp_union;

    ieee_fp_union.w = tvb_get_ntohl(tvb, offset);
    return ieee_fp_union.f;
#endif
}

/*
 * Fetches an IEEE double-precision floating-point number, in
 * big-endian form, and returns a "double".
 */
double tvb_get_ntohieee_double(tvbuff_t *tvb, const int offset)
{
#if defined(vax)
    union {
        uint32_t w[2];
        uint64_t dw;
    } ieee_fp_union;
#else
    union {
        double d;
        uint32_t w[2];
    } ieee_fp_union;
#endif

#if G_BYTE_ORDER == G_BIG_ENDIAN
    ieee_fp_union.w[0] = tvb_get_ntohl(tvb, offset);
    ieee_fp_union.w[1] = tvb_get_ntohl(tvb, offset+4);
#else
    ieee_fp_union.w[0] = tvb_get_ntohl(tvb, offset+4);
    ieee_fp_union.w[1] = tvb_get_ntohl(tvb, offset);
#endif
#if defined(vax)
    return get_ieee_double(ieee_fp_union.dw);
#else
    return ieee_fp_union.d;
#endif
}

uint16_t tvb_get_letohs(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 2);
    return pletoh16(ptr);
}

int16_t tvb_get_letohis(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 2);
    return pletoh16(ptr);
}

uint32_t tvb_get_letoh24(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 3);
    return pletoh24(ptr);
}

int32_t tvb_get_letohi24(tvbuff_t *tvb, const int offset)
{
    uint32_t ret;

    ret = ws_sign_ext32(tvb_get_letoh24(tvb, offset), 24);

    return (int32_t)ret;
}

uint32_t tvb_get_letohl(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 4);
    return pletoh32(ptr);
}

int32_t tvb_get_letohil(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 4);
    return pletoh32(ptr);
}

uint64_t tvb_get_letoh40(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 5);
    return pletoh40(ptr);
}

int64_t tvb_get_letohi40(tvbuff_t *tvb, const int offset)
{
    uint64_t ret;

    ret = ws_sign_ext64(tvb_get_letoh40(tvb, offset), 40);

    return (int64_t)ret;
}

uint64_t tvb_get_letoh48(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 6);
    return pletoh48(ptr);
}

int64_t tvb_get_letohi48(tvbuff_t *tvb, const int offset)
{
    uint64_t ret;

    ret = ws_sign_ext64(tvb_get_letoh48(tvb, offset), 48);

    return (int64_t)ret;
}

uint64_t tvb_get_letoh56(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 7);
    return pletoh56(ptr);
}

int64_t tvb_get_letohi56(tvbuff_t *tvb, const int offset)
{
    uint64_t ret;

    ret = ws_sign_ext64(tvb_get_letoh56(tvb, offset), 56);

    return (int64_t)ret;
}

uint64_t tvb_get_letoh64(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 8);
    return pletoh64(ptr);
}

int64_t tvb_get_letohi64(tvbuff_t *tvb, const int offset)
{
    const uint8_t *ptr;

    ptr = fast_ensure_contiguous(tvb, offset, 8);
    return pletoh64(ptr);
}

/*
 * Fetches an IEEE single-precision floating-point number, in
 * little-endian form, and returns a "float".
 *
 * XXX - should this be "double", in case there are IEEE single-
 * precision numbers that won't fit in some platform's native
 * "float" format?
 */
float tvb_get_letohieee_float(tvbuff_t *tvb, const int offset)
{
#if defined(vax)
    return get_ieee_float(tvb_get_letohl(tvb, offset));
#else
    union {
        float f;
        uint32_t w;
    } ieee_fp_union;

    ieee_fp_union.w = tvb_get_letohl(tvb, offset);
    return ieee_fp_union.f;
#endif
}

/*
 * Fetches an IEEE double-precision floating-point number, in
 * little-endian form, and returns a "double".
 */
double tvb_get_letohieee_double(tvbuff_t *tvb, const int offset)
{
#if defined(vax)
    union {
        uint32_t w[2];
        uint64_t dw;
    } ieee_fp_union;
#else
    union {
        double d;
        uint32_t w[2];
    } ieee_fp_union;
#endif

#if G_BYTE_ORDER == G_BIG_ENDIAN
    ieee_fp_union.w[0] = tvb_get_letohl(tvb, offset+4);
    ieee_fp_union.w[1] = tvb_get_letohl(tvb, offset);
#else
    ieee_fp_union.w[0] = tvb_get_letohl(tvb, offset);
    ieee_fp_union.w[1] = tvb_get_letohl(tvb, offset+4);
#endif
#if defined(vax)
    return get_ieee_double(ieee_fp_union.dw);
#else
    return ieee_fp_union.d;
#endif
}

/* This function is a slight misnomer. It accepts all encodings that are
 * ASCII "enough", which means encodings that are the same as US-ASCII
 * for textual representations of dates and hex bytes; i.e., the same
 * for the hex digits and Z (in practice, all alphanumerics), and the
 * four separators ':' '-' '.' and ' '
 * That means that any encoding that keeps the ISO/IEC 646 invariant
 * characters the same (including the T.61 8 bit encoding and multibyte
 * encodings like EUC-KR and GB18030) are OK, even if they replace characters
 * like '$' '#' and '\' with national variants, but not encodings like UTF-16
 * that include extra null bytes.
 * For our current purposes, the unpacked GSM 7-bit default alphabet (but not
 * all National Language Shift Tables) also satisfies this requirement, but
 * note that it does *not* keep all ISO/IEC 646 invariant characters the same.
 * If this internal function gets used for additional purposes than currently,
 * the set of encodings that it accepts could change.
 * */
static inline void validate_single_byte_ascii_encoding(const unsigned encoding)
{
    const unsigned enc = encoding & ~ENC_CHARENCODING_MASK;

    switch (enc) {
        case ENC_UTF_16:
        case ENC_UCS_2:
        case ENC_UCS_4:
        case ENC_3GPP_TS_23_038_7BITS_PACKED:
        case ENC_ASCII_7BITS:
        case ENC_EBCDIC:
        case ENC_EBCDIC_CP037:
        case ENC_EBCDIC_CP500:
        case ENC_BCD_DIGITS_0_9:
        case ENC_KEYPAD_ABC_TBCD:
        case ENC_KEYPAD_BC_TBCD:
        case ENC_ETSI_TS_102_221_ANNEX_A:
        case ENC_APN_STR:
        case ENC_DECT_STANDARD_4BITS_TBCD:
        REPORT_DISSECTOR_BUG("Invalid string encoding type passed to tvb_get_string_XXX");
        break;
        default:
        break;
    }
    /* make sure something valid was set */
    if (enc == 0)
        REPORT_DISSECTOR_BUG("No string encoding type passed to tvb_get_string_XXX");
}

GByteArray *tvb_get_string_bytes(tvbuff_t *tvb, const int offset, const int length,
             const unsigned encoding, GByteArray *bytes, int *endoff)
{
    char *ptr;
    const char *begin;
    const char *end    = NULL;
    GByteArray  *retval = NULL;

    validate_single_byte_ascii_encoding(encoding);

    ptr = (char*) tvb_get_raw_string(tvb, offset, length);
    begin = ptr;

    if (endoff) *endoff = offset;

    while (*begin == ' ') begin++;

    if (*begin && bytes) {
        if (hex_str_to_bytes_encoding(begin, bytes, &end, encoding, false)) {
            if (bytes->len > 0) {
                if (endoff) *endoff = offset + (int)(end - ptr);
                retval = bytes;
            }
        }
    }

    free(ptr);

    return retval;
}

/*
 * All string functions below take a scope as an argument.
 *
 *
 * If scope is NULL, memory is allocated with g_malloc() and user must
 * explicitly free it with g_free().
 * If scope is not NULL, memory is allocated with the corresponding pool
 * lifetime.
 *
 * All functions throw an exception if the tvbuff ends before the string
 * does.
 */

/*
 * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
 * of bytes referred to by the tvbuff, offset, and length as an ASCII string,
 * with all bytes with the high-order bit set being invalid, and return a
 * pointer to a UTF-8 string, allocated using the wmem scope.
 *
 * Octets with the highest bit set will be converted to the Unicode
 * REPLACEMENT CHARACTER.
 */
static uint8_t *tvb_get_ascii_string(tvbuff_t *tvb, int offset, int length)
{
    const uint8_t *ptr;

    ptr = ensure_contiguous(tvb, offset, length);
    return get_ascii_string(ptr, length);
}

/*
 * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
 * of bytes referred to by the tvbuff, the offset. and the length as a UTF-8
 * string, and return a pointer to a UTF-8 string, allocated using the wmem
 * scope, with all ill-formed sequences replaced with the Unicode REPLACEMENT
 * CHARACTER according to the recommended "best practices" given in the Unicode
 * Standard and specified by W3C/WHATWG.
 *
 * Note that in conformance with the Unicode Standard, this treats three
 * byte sequences corresponding to UTF-16 surrogate halves (paired or unpaired)
 * and two byte overlong encodings of 7-bit ASCII characters as invalid and
 * substitutes REPLACEMENT CHARACTER for them. Explicit support for nonstandard
 * derivative encoding formats (e.g. CESU-8, Java Modified UTF-8, WTF-8) could
 * be added later.
 */
static uint8_t *
tvb_get_utf_8_string(tvbuff_t *tvb, const int offset, const int length)
{
    const uint8_t *ptr;

    ptr = ensure_contiguous(tvb, offset, length);
    return get_utf_8_string(ptr, length);
}

/*
 * Given a wmem scope, a tvbuff, an offset, and a length, treat the string
 * of bytes referred to by the tvbuff, the offset, and the length as a
 * raw string, and return a pointer to that string, allocated using the
 * wmem scope. This means a null is appended at the end, but no replacement
 * checking is done otherwise, unlike tvb_get_utf_8_string().
 *
 * Also, this one allows a length of -1 to mean get all, but does not
 * allow a negative offset.
 */
static inline uint8_t *tvb_get_raw_string(tvbuff_t *tvb, const int offset, const int length)
{
    uint8_t *strbuf;
    int     abs_length = length;

    DISSECTOR_ASSERT(offset     >=  0);
    DISSECTOR_ASSERT(abs_length >= -1);

    if (abs_length < 0)
        abs_length = tvb->length - offset;

    tvb_ensure_bytes_exist(tvb, offset, abs_length);
    strbuf = (uint8_t *)malloc(abs_length + 1);
    tvb_memcpy(tvb, strbuf, offset, abs_length);
    strbuf[abs_length] = '\0';
    return strbuf;
}

/* bits */

static const uint8_t bit_mask8[] = {
    0x00,
    0x01,
    0x03,
    0x07,
    0x0f,
    0x1f,
    0x3f,
    0x7f,
    0xff
};

/* Get a variable amount of bits
 *
 * Return a byte array with bit limited data.
 * When encoding is ENC_BIG_ENDIAN, the data is aligned to the left.
 * When encoding is ENC_LITTLE_ENDIAN, the data is aligned to the right.
 */
uint8_t *tvb_get_bits_array(tvbuff_t *tvb, const int bit_offset,
           size_t no_of_bits, size_t *data_length, const unsigned encoding)
{
    tvbuff_t *sub_tvb;
    if (encoding & ENC_LITTLE_ENDIAN) {
        sub_tvb = tvb_new_octet_right_aligned(tvb, bit_offset, (int32_t) no_of_bits);
    } else {
        sub_tvb = tvb_new_octet_aligned(tvb, bit_offset, (int32_t) no_of_bits);
    }
    *data_length = tvb_reported_length(sub_tvb);
    return (uint8_t*)tvb_memdup(sub_tvb, 0, *data_length);
}

/* Get 1 - 8 bits */
uint8_t tvb_get_bits8(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits)
{
    return (uint8_t)_tvb_get_bits64(tvb, bit_offset, no_of_bits);
}

/* Get 1 - 16 bits */
uint16_t tvb_get_bits16(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits, const unsigned encoding)
{
    return (uint16_t)tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
}

/* Get 1 - 32 bits */
uint32_t tvb_get_bits32(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits, const unsigned encoding)
{
    return (uint32_t)tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
}

/* Get 1 - 64 bits */
uint64_t tvb_get_bits64(tvbuff_t *tvb, unsigned bit_offset, const int no_of_bits, const unsigned encoding)
{
    /* encoding determines bit numbering within octet array */
    if (encoding & ENC_LITTLE_ENDIAN) {
        return _tvb_get_bits64_le(tvb, bit_offset, no_of_bits);
    } else {
        return _tvb_get_bits64(tvb, bit_offset, no_of_bits);
    }
}

/*
 * This function will dissect a sequence of bits that does not need to be byte aligned; the bits
 * set will be shown in the tree as ..10 10.. and the integer value returned if return_value is set.
 * Offset should be given in bits from the start of the tvb.
 * Bits within octet are numbered from MSB (0) to LSB (7). Bit at bit_offset is return value most significant bit.
 * The function tolerates requests for more than 64 bits, but will only return the least significant 64 bits.
 */
static uint64_t _tvb_get_bits64(tvbuff_t *tvb, unsigned bit_offset, const int total_no_of_bits)
{
    uint64_t value;
    unsigned    octet_offset = bit_offset >> 3;
    uint8_t    required_bits_in_first_octet = 8 - (bit_offset % 8);

    if(required_bits_in_first_octet > total_no_of_bits)
    {
        /* the required bits don't extend to the end of the first octet */
        uint8_t right_shift = required_bits_in_first_octet - total_no_of_bits;
        value = (tvb_get_uint8(tvb, octet_offset) >> right_shift) & bit_mask8[total_no_of_bits % 8];
    }
    else
    {
        uint8_t remaining_bit_length = total_no_of_bits;

        /* get the bits up to the first octet boundary */
        value = 0;
        required_bits_in_first_octet %= 8;
        if(required_bits_in_first_octet != 0)
        {
            value = tvb_get_uint8(tvb, octet_offset) & bit_mask8[required_bits_in_first_octet];
            remaining_bit_length -= required_bits_in_first_octet;
            octet_offset ++;
        }
        /* take the biggest words, shorts or octets that we can */
        while (remaining_bit_length > 7)
        {
            switch (remaining_bit_length >> 4)
            {
            case 0:
                /* 8 - 15 bits. (note that 0 - 7 would have dropped out of the while() loop) */
                value <<= 8;
                value += tvb_get_uint8(tvb, octet_offset);
                remaining_bit_length -= 8;
                octet_offset ++;
                break;

            case 1:
                /* 16 - 31 bits */
                value <<= 16;
                value += tvb_get_ntohs(tvb, octet_offset);
                remaining_bit_length -= 16;
                octet_offset += 2;
                break;

            case 2:
            case 3:
                /* 32 - 63 bits */
                value <<= 32;
                value += tvb_get_ntohl(tvb, octet_offset);
                remaining_bit_length -= 32;
                octet_offset += 4;
                break;

            default:
                /* 64 bits (or more???) */
                value = tvb_get_ntoh64(tvb, octet_offset);
                remaining_bit_length -= 64;
                octet_offset += 8;
                break;
            }
        }
        /* get bits from any partial octet at the tail */
        if(remaining_bit_length)
        {
            value <<= remaining_bit_length;
            value += (tvb_get_uint8(tvb, octet_offset) >> (8 - remaining_bit_length));
        }
    }
    return value;
}

/*
 * Offset should be given in bits from the start of the tvb.
 * Bits within octet are numbered from LSB (0) to MSB (7). Bit at bit_offset is return value least significant bit.
 * The function tolerates requests for more than 64 bits, but will only return the least significant 64 bits.
 */
static uint64_t _tvb_get_bits64_le(tvbuff_t *tvb, unsigned bit_offset, const int total_no_of_bits)
{
    uint64_t value = 0;
    unsigned octet_offset = bit_offset / 8;
    int remaining_bits = total_no_of_bits;
    int shift = 0;

    if (remaining_bits > 64)
    {
        remaining_bits = 64;
    }

    if (bit_offset % 8)
    {
        /* not aligned, extract bits from first octet */
        shift = 8 - (bit_offset % 8);
        value = tvb_get_uint8(tvb, octet_offset) >> (bit_offset % 8);
        if (shift > remaining_bits)
        {
            /* keep only the requested bits */
            value &= (UINT64_C(1) << remaining_bits) - 1;
            remaining_bits = 0;
        }
        else
        {
            remaining_bits -= shift;
        }
        octet_offset++;
    }

    while (remaining_bits > 0)
    {
        /* take the biggest words, shorts or octets that we can */
        if (remaining_bits >= 32)
        {
            value |= ((uint64_t)tvb_get_letohl(tvb, octet_offset) << shift);
            shift += 32;
            remaining_bits -= 32;
            octet_offset += 4;
        }
        else if (remaining_bits >= 16)
        {
            value |= ((uint64_t)tvb_get_letohs(tvb, octet_offset) << shift);
            shift += 16;
            remaining_bits -= 16;
            octet_offset += 2;
        }
        else if (remaining_bits >= 8)
        {
            value |= ((uint64_t)tvb_get_uint8(tvb, octet_offset) << shift);
            shift += 8;
            remaining_bits -= 8;
            octet_offset += 1;
        }
        else
        {
            unsigned mask = (1 << remaining_bits) - 1;
            value |= (((uint64_t)tvb_get_uint8(tvb, octet_offset) & mask) << shift);
            shift += remaining_bits;
            remaining_bits = 0;
            octet_offset += 1;
        }
    }
    return value;
}

/* Get 1 - 32 bits (should be deprecated as same as tvb_get_bits32??) */
uint32_t tvb_get_bits(tvbuff_t *tvb, const unsigned bit_offset, const int no_of_bits, const unsigned encoding)
{
    return (uint32_t)tvb_get_bits64(tvb, bit_offset, no_of_bits, encoding);
}

/* bits end */


/* find uint */

static int tvb_find_uint8_generic(tvbuff_t *tvb, unsigned abs_offset, unsigned limit, uint8_t needle)
{
    const uint8_t *ptr;
    const uint8_t *result;

    ptr = ensure_contiguous(tvb, abs_offset, limit); /* tvb_get_ptr() */
    if (!ptr)
        return -1;

    result = (const uint8_t *) memchr(ptr, needle, limit);
    if (!result)
        return -1;

    return (int) ((result - ptr) + abs_offset);
}

/* Find first occurrence of needle in tvbuff, starting at offset. Searches
 * at most maxlength number of bytes; if maxlength is -1, searches to
 * end of tvbuff.
 * Returns the offset of the found needle, or -1 if not found.
 * Will not throw an exception, even if maxlength exceeds boundary of tvbuff;
 * in that case, -1 will be returned if the boundary is reached before
 * finding needle. */
int tvb_find_uint8(tvbuff_t *tvb, const int offset, const int maxlength, const uint8_t needle)
{
    const uint8_t *result;
    unsigned          abs_offset = 0;
    unsigned          limit = 0;
    int           exception;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
    if (exception)
        return 0;

    /* Only search to end of tvbuff, w/o throwing exception. */
    if (maxlength >= 0 && limit > (unsigned) maxlength) {
        /* Maximum length doesn't go past end of tvbuff; search
           to that value. */
        limit = (unsigned) maxlength;
    }

    /* If we have real data, perform our search now. */
    if (tvb->real_data) {
        result = (const uint8_t *)memchr(tvb->real_data + abs_offset, needle, limit);
        if (result == NULL) {
            return -1;
        } else {
            return (int) (result - tvb->real_data);
        }
    }

    if (tvb->ops->tvb_find_uint8)
        return tvb->ops->tvb_find_uint8(tvb, abs_offset, limit, needle);

    return tvb_find_uint8_generic(tvb, offset, limit, needle);
}

/* Same as tvb_find_uint8() with 16bit needle. */
int tvb_find_uint16(tvbuff_t *tvb, const int offset, const int maxlength,
         const uint16_t needle)
{
    unsigned          abs_offset = 0;
    unsigned          limit = 0;
    int           exception;

    exception = compute_offset_and_remaining(tvb, offset, &abs_offset, &limit);
    if (exception)
        return 0;

    /* Only search to end of tvbuff, w/o throwing exception. */
    if (maxlength >= 0 && limit > (unsigned) maxlength) {
        /* Maximum length doesn't go past end of tvbuff; search
           to that value. */
        limit = (unsigned) maxlength;
    }

    const uint8_t needle1 = ((needle & 0xFF00) >> 8);
    const uint8_t needle2 = ((needle & 0x00FF) >> 0);
    unsigned searched_bytes = 0;
    unsigned pos = abs_offset;

    do {
        int offset1 = tvb_find_uint8(tvb, pos, limit - searched_bytes, needle1);
        int offset2 = -1;

        if (offset1 == -1) {
            return -1;
        }

        searched_bytes = (unsigned)offset1 - abs_offset + 1;

        if (searched_bytes >= limit) {
            return -1;
        }

        offset2 = tvb_find_uint8(tvb, offset1 + 1, 1, needle2);

        searched_bytes += 1;

        if (offset2 != -1) {
            if (searched_bytes > limit) {
                return -1;
            }
            return offset1;
        }

        pos = offset1 + 1;
    } while (searched_bytes < limit);

    return -1;
}

/* find uint end */

/* Find size of stringz (NUL-terminated string) by looking for terminating
 * NUL.  The size of the string includes the terminating NUL.
 *
 * If the NUL isn't found, it throws the appropriate exception.
 */
unsigned tvb_strsize(tvbuff_t *tvb, const int offset)
{
    unsigned abs_offset = 0, junk_length;
    int   nul_offset;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);
    nul_offset = tvb_find_uint8(tvb, abs_offset, -1, 0);
    if (nul_offset == -1) {
        /*
         * OK, we hit the end of the tvbuff, so we should throw
         * an exception.
         */
//        if (tvb->length < tvb->contained_length) {
//            THROW(BoundsError);
//        } else if (tvb->flags & TVBUFF_FRAGMENT) {
//            THROW(FragmentBoundsError);
//        } else if (tvb->length < tvb->reported_length) {
//            THROW(ContainedBoundsError);
//        } else {
//            THROW(ReportedBoundsError);
//        }
    }
    return (nul_offset - abs_offset) + 1;
}

/* UTF-16/UCS-2 version of tvb_strsize */
/* Returns number of bytes including the (two-bytes) null terminator */
unsigned tvb_unicode_strsize(tvbuff_t *tvb, const int offset)
{
    unsigned  i = 0;
    gunichar2 uchar;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    do {
        /* Endianness doesn't matter when looking for null */
        uchar = tvb_get_ntohs(tvb, offset + i);
        i += 2;
    } while(uchar != 0);

    return i;
}

/* Find length of string by looking for end of string ('\0'), up to
 * 'maxlength' characters'; if 'maxlength' is -1, searches to end
 * of tvbuff.
 * Returns -1 if 'maxlength' reached before finding EOS. */
int tvb_strnlen(tvbuff_t *tvb, const int offset, const unsigned maxlength)
{
    int   result_offset;
    unsigned abs_offset = 0, junk_length;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    check_offset_length(tvb, offset, 0, &abs_offset, &junk_length);

    result_offset = tvb_find_uint8(tvb, abs_offset, maxlength, 0);

    if (result_offset == -1) {
        return -1;
    }
    else {
        return result_offset - abs_offset;
    }
}

/**
 * Format the data in the tvb from offset for size.
 */
char *tvb_format_text(tvbuff_t *tvb, const int offset, const int size)
{
    const uint8_t *ptr;
    int           len;

    len = (size > 0) ? size : 0;

    ptr = ensure_contiguous(tvb, offset, size);
    return format_text(ptr, len);
}

/*
* Given a tvbuff, an offset, a length, and an encoding, allocate a
* buffer big enough to hold a non-null-terminated string of that length
* at that offset, plus a trailing '\0', copy into the buffer the
* string as converted from the appropriate encoding to UTF-8, and
* return a pointer to the string.
*/
uint8_t *tvb_get_string_enc(tvbuff_t *tvb, const int offset,
                const int length, const unsigned encoding)
{
   uint8_t *strptr;
   bool odd, skip_first;

   DISSECTOR_ASSERT(tvb && tvb->initialized);

   /* make sure length = -1 fails */
//   if (length < 0) {
//       THROW(ReportedBoundsError);
//   }

   switch (encoding & ENC_CHARENCODING_MASK) {

   case ENC_ASCII:
   default:
       /*
        * For now, we treat bogus values as meaning
        * "ASCII" rather than reporting an error,
        * for the benefit of old dissectors written
        * when the last argument to proto_tree_add_item()
        * was a bool for the byte order, not an
        * encoding value, and passed non-zero values
        * other than true to mean "little-endian".
        */
       strptr = tvb_get_ascii_string(tvb, offset, length);
       break;

   case ENC_UTF_8:
       strptr = tvb_get_utf_8_string(tvb, offset, length);
       break;

//   case ENC_UTF_16:
//       strptr = tvb_get_utf_16_string(scope, tvb, offset, length,
//           encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
//       break;

//   case ENC_UCS_2:
//       strptr = tvb_get_ucs_2_string(scope, tvb, offset, length,
//           encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
//       break;

//   case ENC_UCS_4:
//       strptr = tvb_get_ucs_4_string(scope, tvb, offset, length,
//           encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
//       break;

//   case ENC_ISO_8859_1:
//       /*
//        * ISO 8859-1 printable code point values are equal
//        * to the equivalent Unicode code point value, so
//        * no translation table is needed.
//        */
//       strptr = tvb_get_string_8859_1(scope, tvb, offset, length);
//       break;

//   case ENC_ISO_8859_2:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_2);
//       break;

//   case ENC_ISO_8859_3:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_3);
//       break;

//   case ENC_ISO_8859_4:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_4);
//       break;

//   case ENC_ISO_8859_5:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_5);
//       break;

//   case ENC_ISO_8859_6:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_6);
//       break;

//   case ENC_ISO_8859_7:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_7);
//       break;

//   case ENC_ISO_8859_8:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_8);
//       break;

//   case ENC_ISO_8859_9:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_9);
//       break;

//   case ENC_ISO_8859_10:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_10);
//       break;

//   case ENC_ISO_8859_11:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_11);
//       break;

//   case ENC_ISO_8859_13:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_13);
//       break;

//   case ENC_ISO_8859_14:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_14);
//       break;

//   case ENC_ISO_8859_15:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_15);
//       break;

//   case ENC_ISO_8859_16:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_iso_8859_16);
//       break;

//   case ENC_WINDOWS_1250:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1250);
//       break;

//   case ENC_WINDOWS_1251:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1251);
//       break;

//   case ENC_WINDOWS_1252:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp1252);
//       break;

//   case ENC_MAC_ROMAN:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_mac_roman);
//       break;

//   case ENC_CP437:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp437);
//       break;

//   case ENC_CP855:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp855);
//       break;

//   case ENC_CP866:
//       strptr = tvb_get_string_unichar2(scope, tvb, offset, length, charset_table_cp866);
//       break;

//   case ENC_ISO_646_BASIC:
//       strptr = tvb_get_iso_646_string(scope, tvb, offset, length, charset_table_iso_646_basic);
//       break;

//   case ENC_3GPP_TS_23_038_7BITS_PACKED:
//       {
//           int bit_offset  = offset << 3;
//           int no_of_chars = (length << 3) / 7;
//           strptr = tvb_get_ts_23_038_7bits_string_packed(scope, tvb, bit_offset, no_of_chars);
//       }
//       break;

//   case ENC_ASCII_7BITS:
//       {
//           int bit_offset  = offset << 3;
//           int no_of_chars = (length << 3) / 7;
//           strptr = tvb_get_ascii_7bits_string(scope, tvb, bit_offset, no_of_chars);
//       }
//       break;

//   case ENC_EBCDIC:
//       /*
//        * "Common" EBCDIC, covering all characters with the
//        * same code point in all Roman-alphabet EBCDIC code
//        * pages.
//        */
//       strptr = tvb_get_nonascii_unichar2_string(scope, tvb, offset, length, charset_table_ebcdic);
//       break;

//   case ENC_EBCDIC_CP037:
//       /*
//        * EBCDIC code page 037.
//        */
//       strptr = tvb_get_nonascii_unichar2_string(scope, tvb, offset, length, charset_table_ebcdic_cp037);
//       break;

//   case ENC_EBCDIC_CP500:
//       /*
//        * EBCDIC code page 500.
//        */
//       strptr = tvb_get_nonascii_unichar2_string(scope, tvb, offset, length, charset_table_ebcdic_cp500);
//       break;

//   case ENC_T61:
//       strptr = tvb_get_t61_string(scope, tvb, offset, length);
//       break;

//   case ENC_BCD_DIGITS_0_9:
//       /*
//        * Packed BCD, with digits 0-9.
//        */
//       odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
//       skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
//       strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt0_9_bcd, skip_first, odd, !(encoding & ENC_LITTLE_ENDIAN));
//       break;

//   case ENC_KEYPAD_ABC_TBCD:
//       /*
//        * Keypad-with-a/b/c "telephony BCD" - packed BCD, with
//        * digits 0-9 and symbols *, #, a, b, and c.
//        */
//       odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
//       skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
//       strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt_keypad_abc_tbcd, skip_first, odd, !(encoding & ENC_LITTLE_ENDIAN));
//       break;

//   case ENC_KEYPAD_BC_TBCD:
//       /*
//        * Keypad-with-B/C "telephony BCD" - packed BCD, with
//        * digits 0-9 and symbols B, C, *, and #.
//        */
//       odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
//       skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
//       strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt_ansi_tbcd, skip_first, odd, !(encoding & ENC_LITTLE_ENDIAN));
//       break;

//   case ENC_3GPP_TS_23_038_7BITS_UNPACKED:
//       strptr = tvb_get_ts_23_038_7bits_string_unpacked(scope, tvb, offset, length);
//       break;

//   case ENC_ETSI_TS_102_221_ANNEX_A:
//       strptr = tvb_get_etsi_ts_102_221_annex_a_string(scope, tvb, offset, length);
//       break;

//   case ENC_GB18030:
//       strptr = tvb_get_gb18030_string(scope, tvb, offset, length);
//       break;

//   case ENC_EUC_KR:
//       strptr = tvb_get_euc_kr_string(scope, tvb, offset, length);
//       break;

//   case ENC_APN_STR:
//       strptr = tvb_get_apn_string(scope, tvb, offset, length);
//       break;

//   case ENC_DECT_STANDARD_8BITS:
//       strptr = tvb_get_dect_standard_8bits_string(scope, tvb, offset, length);
//       break;

//   case ENC_DECT_STANDARD_4BITS_TBCD:
//       /*
//        * DECT standard 4bits "telephony BCD" - packed BCD, with
//        * digits 0-9 and symbol SPACE for 0xb.
//        */
//       odd = (encoding & ENC_BCD_ODD_NUM_DIG) >> 16;
//       skip_first = (encoding & ENC_BCD_SKIP_FIRST) >> 17;
//       strptr = tvb_get_bcd_string(scope, tvb, offset, length, &Dgt_dect_standard_4bits_tbcd, skip_first, odd, false);
//       break;
   }
   return strptr;
}

/*
* This is like tvb_get_string_enc(), except that it handles null-padded
* strings.
*
* Currently, string values are stored as UTF-8 null-terminated strings,
* so nothing needs to be done differently for null-padded strings; we
* could save a little memory by not storing the null padding.
*
* If we ever store string values differently, in a fashion that doesn't
* involve null termination, that might change.
*/
uint8_t *tvb_get_stringzpad(tvbuff_t *tvb, const int offset,
          const int length, const unsigned encoding)
{
   return tvb_get_string_enc(tvb, offset, length, encoding);
}

/*
* These routines are like the above routines, except that they handle
* null-terminated strings.  They find the length of that string (and
* throw an exception if the tvbuff ends before we find the null), and
* also return through a pointer the length of the string, in bytes,
* including the terminating null (the terminating null being 2 bytes
* for UCS-2 and UTF-16, 4 bytes for UCS-4, and 1 byte for other
* encodings).
*/
static uint8_t *tvb_get_ascii_stringz(tvbuff_t *tvb, int offset, int *lengthp)
{
   unsigned           size;
   const uint8_t *ptr;

   size = tvb_strsize(tvb, offset);
   ptr  = ensure_contiguous(tvb, offset, size);
   /* XXX, conversion between signed/unsigned integer */
   if (lengthp)
       *lengthp = size;
   return get_ascii_string(ptr, size);
}

static uint8_t *tvb_get_utf_8_stringz(tvbuff_t *tvb, const int offset, int *lengthp)
{
    unsigned   size;
    const uint8_t *ptr;

    size   = tvb_strsize(tvb, offset);
    ptr = ensure_contiguous(tvb, offset, size);
    /* XXX, conversion between signed/unsigned integer */
    if (lengthp)
        *lengthp = size;
    return get_utf_8_string(ptr, size);
}

uint8_t *tvb_get_stringz_enc(tvbuff_t *tvb, const int offset, int *lengthp, const unsigned encoding)
{
    uint8_t *strptr;

    DISSECTOR_ASSERT(tvb && tvb->initialized);

    switch (encoding & ENC_CHARENCODING_MASK) {

    case ENC_ASCII:
    default:
        /*
         * For now, we treat bogus values as meaning
         * "ASCII" rather than reporting an error,
         * for the benefit of old dissectors written
         * when the last argument to proto_tree_add_item()
         * was a bool for the byte order, not an
         * encoding value, and passed non-zero values
         * other than true to mean "little-endian".
         */
        strptr = tvb_get_ascii_stringz(tvb, offset, lengthp);
        break;

    case ENC_UTF_8:
        /*
         * XXX - should map all invalid UTF-8 sequences
         * to a "substitute" UTF-8 character.
         * XXX - should map code points > 10FFFF to REPLACEMENT
         * CHARACTERs.
         */
        strptr = tvb_get_utf_8_stringz(tvb, offset, lengthp);
        break;

//    case ENC_UTF_16:
//        strptr = tvb_get_utf_16_stringz(scope, tvb, offset, lengthp,
//            encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
//        break;

//    case ENC_UCS_2:
//        strptr = tvb_get_ucs_2_stringz(scope, tvb, offset, lengthp,
//            encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
//        break;

//    case ENC_UCS_4:
//        strptr = tvb_get_ucs_4_stringz(scope, tvb, offset, lengthp,
//            encoding & (ENC_LITTLE_ENDIAN|ENC_BOM));
//        break;

//    case ENC_ISO_8859_1:
//        /*
//         * ISO 8859-1 printable code point values are equal
//         * to the equivalent Unicode code point value, so
//         * no translation table is needed.
//         */
//        strptr = tvb_get_stringz_8859_1(scope, tvb, offset, lengthp);
//        break;

//    case ENC_ISO_8859_2:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_2);
//        break;

//    case ENC_ISO_8859_3:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_3);
//        break;

//    case ENC_ISO_8859_4:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_4);
//        break;

//    case ENC_ISO_8859_5:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_5);
//        break;

//    case ENC_ISO_8859_6:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_6);
//        break;

//    case ENC_ISO_8859_7:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_7);
//        break;

//    case ENC_ISO_8859_8:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_8);
//        break;

//    case ENC_ISO_8859_9:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_9);
//        break;

//    case ENC_ISO_8859_10:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_10);
//        break;

//    case ENC_ISO_8859_11:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_11);
//        break;

//    case ENC_ISO_8859_13:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_13);
//        break;

//    case ENC_ISO_8859_14:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_14);
//        break;

//    case ENC_ISO_8859_15:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_15);
//        break;

//    case ENC_ISO_8859_16:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_iso_8859_16);
//        break;

//    case ENC_WINDOWS_1250:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1250);
//        break;

//    case ENC_WINDOWS_1251:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1251);
//        break;

//    case ENC_WINDOWS_1252:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp1252);
//        break;

//    case ENC_MAC_ROMAN:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_mac_roman);
//        break;

//    case ENC_CP437:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp437);
//        break;

//    case ENC_CP855:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp855);
//        break;

//    case ENC_CP866:
//        strptr = tvb_get_stringz_unichar2(scope, tvb, offset, lengthp, charset_table_cp866);
//        break;

//    case ENC_ISO_646_BASIC:
//        strptr = tvb_get_iso_646_stringz(scope, tvb, offset, lengthp, charset_table_iso_646_basic);
//        break;

//    case ENC_3GPP_TS_23_038_7BITS_PACKED:
//    case ENC_3GPP_TS_23_038_7BITS_UNPACKED:
//    case ENC_ETSI_TS_102_221_ANNEX_A:
//        REPORT_DISSECTOR_BUG("TS 23.038 7bits has no null character and doesn't support null-terminated strings");
//        break;

//    case ENC_ASCII_7BITS:
//        REPORT_DISSECTOR_BUG("tvb_get_stringz_enc function with ENC_ASCII_7BITS not implemented yet");
//        break;

//    case ENC_EBCDIC:
//        /*
//         * "Common" EBCDIC, covering all characters with the
//         * same code point in all Roman-alphabet EBCDIC code
//         * pages.
//         */
//        strptr = tvb_get_nonascii_unichar2_stringz(scope, tvb, offset, lengthp, charset_table_ebcdic);
//        break;

//    case ENC_EBCDIC_CP037:
//        /*
//         * EBCDIC code page 037.
//         */
//        strptr = tvb_get_nonascii_unichar2_stringz(scope, tvb, offset, lengthp, charset_table_ebcdic_cp037);
//        break;

//    case ENC_EBCDIC_CP500:
//        /*
//         * EBCDIC code page 500.
//         */
//        strptr = tvb_get_nonascii_unichar2_stringz(scope, tvb, offset, lengthp, charset_table_ebcdic_cp500);
//        break;

//    case ENC_T61:
//        strptr = tvb_get_t61_stringz(scope, tvb, offset, lengthp);
//        break;

//    case ENC_GB18030:
//        strptr = tvb_get_gb18030_stringz(scope, tvb, offset, lengthp);
//        break;

//    case ENC_EUC_KR:
//        strptr = tvb_get_euc_kr_stringz(scope, tvb, offset, lengthp);
//        break;

//    case ENC_DECT_STANDARD_8BITS:
//        strptr = tvb_get_dect_standard_8bits_stringz(scope, tvb, offset, lengthp);
//        break;
    }

    return strptr;
}

/*
 * Format a bunch of data from a tvbuff as bytes, returning a pointer
 * to the string with the formatted data.
 */
char *tvb_bytes_to_str(tvbuff_t *tvb, const int offset, const int len)
{
    return bytes_to_str(ensure_contiguous(tvb, offset, len), len);
}

/* Find a needle tvbuff within a haystack tvbuff. */
int tvb_find_tvb(tvbuff_t *haystack_tvb, tvbuff_t *needle_tvb, const int haystack_offset)
{
    unsigned          haystack_abs_offset = 0, haystack_abs_length = 0;
    const uint8_t *haystack_data;
    const uint8_t *needle_data;
    const unsigned   needle_len = needle_tvb->length;
    const uint8_t *location;

    DISSECTOR_ASSERT(haystack_tvb && haystack_tvb->initialized);

    if (haystack_tvb->length < 1 || needle_tvb->length < 1) {
        return -1;
    }

    /* Get pointers to the tvbuffs' data. */
    haystack_data = ensure_contiguous(haystack_tvb, 0, -1);
    needle_data   = ensure_contiguous(needle_tvb, 0, -1);

    check_offset_length(haystack_tvb, haystack_offset, -1,
            &haystack_abs_offset, &haystack_abs_length);

    location = ws_memmem(haystack_data + haystack_abs_offset, haystack_abs_length,
            needle_data, needle_len);

    if (location) {
        return (int) (location - haystack_data);
    }

    return -1;
}

int tvb_raw_offset(tvbuff_t *tvb)
{
    return ((tvb->raw_offset==-1) ? (tvb->raw_offset = tvb_offset_from_real_beginning(tvb)) : tvb->raw_offset);
}

void tvb_set_fragment(tvbuff_t *tvb)
{
    tvb->flags |= TVBUFF_FRAGMENT;
}

struct tvbuff *tvb_get_ds_tvb(tvbuff_t *tvb)
{
    return(tvb->ds_tvb);
}

unsigned tvb_get_varint(tvbuff_t *tvb, unsigned offset, unsigned maxlen, uint64_t *value, const unsigned encoding)
{
    *value = 0;

    switch (encoding & ENC_VARINT_MASK) {
    case ENC_VARINT_PROTOBUF:
    {
        unsigned i;
        uint64_t b; /* current byte */

        for (i = 0; ((i < FT_VARINT_MAX_LEN) && (i < maxlen)); ++i) {
            b = tvb_get_uint8(tvb, offset++);
            *value |= ((b & 0x7F) << (i * 7)); /* add lower 7 bits to val */

            if (b < 0x80) {
                /* end successfully becauseof last byte's msb(most significant bit) is zero */
                return i + 1;
            }
        }
        break;
    }

    case ENC_VARINT_ZIGZAG:
    {
        unsigned i;
        uint64_t b; /* current byte */

        for (i = 0; ((i < FT_VARINT_MAX_LEN) && (i < maxlen)); ++i) {
            b = tvb_get_uint8(tvb, offset++);
            *value |= ((b & 0x7F) << (i * 7)); /* add lower 7 bits to val */

            if (b < 0x80) {
                /* end successfully becauseof last byte's msb(most significant bit) is zero */
                *value = (*value >> 1) ^ ((*value & 1) ? -1 : 0);
                return i + 1;
            }
        }
        break;
    }

    case ENC_VARINT_SDNV:
    {
        /* Decodes similar to protobuf but in MSByte order */
        unsigned i;
        uint64_t b; /* current byte */

        for (i = 0; ((i < FT_VARINT_MAX_LEN) && (i < maxlen)); ++i) {
            b = tvb_get_uint8(tvb, offset++);
            if ((i == 9) && (*value >= UINT64_C(1)<<(64-7))) {
                // guaranteed overflow, not valid SDNV
                return 0;
            }
            *value <<= 7;
            *value |= (b & 0x7F); /* add lower 7 bits to val */

            if (b < 0x80) {
                /* end successfully because of last byte's msb(most significant bit) is zero */
                return i + 1;
            }
        }
        break;
    }

    case ENC_VARINT_QUIC:
    {
        /* calculate variable length */
        *value = tvb_get_uint8(tvb, offset);
        switch((*value) >> 6) {
        case 0: /* 0b00 => 1 byte length (6 bits Usable) */
            (*value) &= 0x3F;
            return 1;
        case 1: /* 0b01 => 2 bytes length (14 bits Usable) */
            *value = tvb_get_ntohs(tvb, offset) & 0x3FFF;
            return 2;
        case 2: /* 0b10 => 4 bytes length (30 bits Usable) */
            *value = tvb_get_ntohl(tvb, offset) & 0x3FFFFFFF;
            return 4;
        case 3: /* 0b11 => 8 bytes length (62 bits Usable) */
            *value = tvb_get_ntoh64(tvb, offset) & UINT64_C(0x3FFFFFFFFFFFFFFF);
            return 8;
        default: /* No Possible */
            break;
        }
        break;
    }

    default:
        DISSECTOR_ASSERT_NOT_REACHED();
    }

    return 0; /* 10 bytes scanned, but no bytes' msb is zero */
}
